#pragma once 
#include <stdint.h>
#include <ostream>
#include <vector>

enum NET_ADDRESSES {
  NA_IPV4,
  NA_IPV6,
  NA_MAC,
  NA_INVALID
};

typedef uint32_t IpV4Addr;
typedef uint8_t IpV6Addr[16];

struct NetAddress {
  NetAddress() 
    : type(NA_INVALID)
  {}
  
  NetAddress(NET_ADDRESSES type, uint32_t value) 
    : type(type) {
    *((uint32_t*)&addr) = value;
  }
  
  NetAddress(NET_ADDRESSES type, uint32_t value, uint32_t value2) 
    : type(type) {
    *((uint32_t*)&addr) = value;
    *(((uint32_t*)&addr) + sizeof(uint32_t)) = value2;
  }

  bool isIpV4Addr() const {
    return type == NA_IPV4;
  }

  IpV4Addr getIpV4Addr() const {
    return *((IpV4Addr*)&addr);
  }

  IpV4Addr getIpV4NetMask() const {
    return  *(((uint32_t*)&addr) + sizeof(uint32_t));
  }

  bool operator==(const NetAddress& a) const {
    return getIpV4Addr() == a.getIpV4Addr();
  }
  
  bool operator<(const NetAddress& a) const {
    return getIpV4Addr() < a.getIpV4Addr();
  }

  NetAddress toLittleEndian() const {
    NetAddress ret = *this;
    size_t addrSize = 4;
    for(size_t i = 0 ; i < addrSize; ++i) {
      ret.addr[i] = addr[addrSize - i - 1];
    }
    return ret;
  }

private:
  uint8_t addr[16];
  NET_ADDRESSES type;
};

inline std::ostream& operator<<(std::ostream& out, const NetAddress& addr) {
  IpV4Addr aip = addr.getIpV4Addr();
  uint8_t *a = (uint8_t*)&aip;
  return out << (size_t)a[0] << "." << (size_t)a[1] << "." << (size_t)a[2] << "." << (size_t)a[3];
}

typedef std::vector<NetAddress>  NetAddresses;
